//
//  CLLocationCoordinate2D+change.swift
//  map
//
//  Created by fyzq on 2024/2/1.
//

import Foundation

extension CLLocationCoordinate2D {
    //定义一些常量
    fileprivate struct CoordinateType {
        static var CoordinateTypePI: Void?
        static var CoordinateTypeXPI: Void?
        static var CoordinateTypeA: Void?
        static var CoordinateTypeEE: Void?
    }
    public var x_PI: Double {
        get {
            guard let obj = objc_getAssociatedObject(self, &CoordinateType.CoordinateTypeXPI) as? Double else { return 3.14159265358979324 * 3000.0 / 180.0 }
            return obj
        }
        set {
            objc_setAssociatedObject(self, &CoordinateType.CoordinateTypeXPI, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    public var PI: Double {
        get {
            guard let obj = objc_getAssociatedObject(self, &CoordinateType.CoordinateTypePI) as? Double else { return 3.1415926535897932384626 }
            return obj
        }
        set {
            objc_setAssociatedObject(self, &CoordinateType.CoordinateTypePI, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    public var a: Double {
        get {
            guard let obj = objc_getAssociatedObject(self, &CoordinateType.CoordinateTypeA) as? Double else { return 6378245.0 }
            return obj
        }
        set {
            objc_setAssociatedObject(self, &CoordinateType.CoordinateTypeA, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    public var ee: Double {
        get {
            guard let obj = objc_getAssociatedObject(self, &CoordinateType.CoordinateTypeA) as? Double else { return 0.00669342162296594323 }
            return obj
        }
        set {
            objc_setAssociatedObject(self, &CoordinateType.CoordinateTypeA, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    
    /**
     * 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换
     * 即 百度 转 谷歌、高德
     * @param bd_lon
     * @param bd_lat
     * @returns {*[]}
     */
    func bd09togcj02() -> CLLocationCoordinate2D {
        let bd_lon = self.longitude
        let bd_lat = self.latitude
        let x_pi = 3.14159265358979324 * 3000.0 / 180.0;
        let x = bd_lon - 0.0065;
        let y = bd_lat - 0.006;
        
        let z = sqrt(x * x + y * y) - 0.00002 * sin(y * x_pi);
        let theta = atan2(y, x) - 0.000003 * cos(x * x_pi);
        let gg_lng = z * cos(theta);
        let gg_lat = z * sin(theta);
        return CLLocationCoordinate2D(latitude: gg_lat, longitude: gg_lng)
    }
    
    /**
     WGS84转百度坐标系 (BD-09)
     */
    
    func wgs84ToBd09() -> CLLocationCoordinate2D {
        
        let lat = self.latitude
        var bd_lon = self.longitude
        var bd_lat = self.latitude
          
        bd_lon -= 0.0065
        bd_lat -= 0.006
          
        let z = sqrt(bd_lon * bd_lon + bd_lat * bd_lat) - a * ee
        let theta = atan(z / (1 - ee))
        let rho = (1 - ee) * a * a * (1 - z) / (z * z)
        let x = (1 - ee) * a * (1 + rho * cos(theta) * cos(theta) * cos(theta)) / z
        let y = (1 - ee) * a * (1 + rho * sin(theta) * sin(theta) * sin(theta)) / z
          
        bd_lon += x / cos(lat)
        bd_lat += y / cos(lat)
        return CLLocationCoordinate2D(latitude: bd_lat, longitude: bd_lon)
    }
    
    
    /**
     * WGS84转GCj02
     * @param lng
     * @param lat
     * @returns {*[]}
     */
    func wgs84togcj02() -> CLLocationCoordinate2D{
        let lng = self.longitude
        let lat = self.latitude
        
        if (self.out_of_china(lng: lng, lat: lat)) {
            return CLLocationCoordinate2D(latitude: lat, longitude: lng)
        }
        else {
            var dlat = transformlat(lng: lng - 105.0, lat: lat - 35.0);
            var dlng = transformlng(lng: lng - 105.0, lat: lat - 35.0);
            var radlat = lat / 180.0 * PI;
            var magic = sin(radlat);
            magic = 1 - ee * magic * magic;
            var sqrtmagic = sqrt(magic);
            dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
            dlng = (dlng * 180.0) / (a / sqrtmagic * cos(radlat) * PI);
            var mglat = lat + dlat;
            var mglng = lng + dlng;
            return CLLocationCoordinate2D(latitude: mglat, longitude: mglng)
        }
    }

    
    func transformlat(lng:Double, lat:Double) -> Double {
        var ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * sqrt(abs(lng));
        ret += (20.0 * sin(6.0 * lng * PI) + 20.0 * sin(2.0 * lng * PI)) * 2.0 / 3.0;
        ret += (20.0 * sin(lat * PI) + 40.0 * sin(lat / 3.0 * PI)) * 2.0 / 3.0;
        ret += (160.0 * sin(lat / 12.0 * PI) + 320 * sin(lat * PI / 30.0)) * 2.0 / 3.0;
        return ret
    }

    func transformlng(lng:Double, lat:Double) -> Double {
        var ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * sqrt(abs(lng));
        ret += (20.0 * sin(6.0 * lng * PI) + 20.0 * sin(2.0 * lng * PI)) * 2.0 / 3.0;
        ret += (20.0 * sin(lng * PI) + 40.0 * sin(lng / 3.0 * PI)) * 2.0 / 3.0;
        ret += (150.0 * sin(lng / 12.0 * PI) + 300.0 * sin(lng / 30.0 * PI)) * 2.0 / 3.0;
        return ret
    }
    

    

    /**
     * GCJ02 转换为 WGS84
     * @param lng
     * @param lat
     * @returns {*[]}
     */
    func gcj02towgs84(lng:Double, lat:Double)->CLLocationCoordinate2D {
        if (self.out_of_china(lng: lng, lat: lat)) {
            return CLLocationCoordinate2D(latitude: lat, longitude: lng)
        }
        else {
            var dlat = transformlat(lng: lng - 105.0, lat: lat - 35.0);
            var dlng = transformlng(lng: lng - 105.0, lat: lat - 35.0);
            var radlat = lat / 180.0 * PI;
            var magic = sin(radlat);
            magic = 1 - ee * magic * magic;
            var sqrtmagic = sqrt(magic);
            dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
            dlng = (dlng * 180.0) / (a / sqrtmagic * cos(radlat) * PI);
            var mglat = lat + dlat;
            var mglng = lng + dlng;
            return CLLocationCoordinate2D(latitude: lat * 2 - mglat, longitude: lng * 2 - mglng)
        }
    }
    
    /**
     * 判断是否在国内，不在国内则不做偏移
     * @param lng
     * @param lat
     * @returns {boolean}
     */
    func out_of_china(lng:Double, lat:Double) -> Bool {
        return (lng < 72.004 || lng > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false);
    }
    

    
}
